#include "logger.h"
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>

// Global logger instance
logger_t* g_logger = NULL;

// Log level strings
static const char* LOG_LEVEL_STRINGS[] = {
    "DEBUG", "INFO", "WARN", "ERROR"
};

// Create logger instance
logger_t* logger_create(const logger_config_t* config) {
    if (!config) {
        return NULL;
    }
    
    logger_t* logger = calloc(1, sizeof(logger_t));
    if (!logger) {
        return NULL;
    }
    
    // Copy configuration
    memcpy(&logger->config, config, sizeof(logger_config_t));
    
    // Initialize mutex
    if (pthread_mutex_init(&logger->mutex, NULL) != 0) {
        free(logger);
        return NULL;
    }
    
    logger->start_time = time(NULL);
    logger->initialized = false;
    
    return logger;
}

// Initialize logger (open log file)
int logger_init(logger_t* logger) {
    if (!logger || logger->initialized) {
        return -1;
    }
    
    pthread_mutex_lock(&logger->mutex);
    
    // Open log file in append mode
    logger->log_file = fopen(logger->config.log_file_path, "a");
    if (!logger->log_file) {
        pthread_mutex_unlock(&logger->mutex);
        return -1;
    }
    
    // Get current file size
    fseek(logger->log_file, 0, SEEK_END);
    logger->current_file_size = ftell(logger->log_file);
    
    logger->initialized = true;
    
    pthread_mutex_unlock(&logger->mutex);
    
    // Log initialization
    logger_log(logger, LOG_LEVEL_INFO, "LOGGER", 
               "Logger initialized: file=%s, level=%s, max_size=%llu MB",
               logger->config.log_file_path,
               log_level_to_string(logger->config.min_level),
               logger->config.max_file_size / (1024 * 1024));
    
    return 0;
}

// Destroy logger
void logger_destroy(logger_t* logger) {
    if (!logger) {
        return;
    }
    
    if (logger->initialized) {
        logger_log(logger, LOG_LEVEL_INFO, "LOGGER", "Logger shutting down");
        
        pthread_mutex_lock(&logger->mutex);
        
        if (logger->log_file) {
            fclose(logger->log_file);
            logger->log_file = NULL;
        }
        
        pthread_mutex_unlock(&logger->mutex);
    }
    
    pthread_mutex_destroy(&logger->mutex);
    free(logger);
    
    if (g_logger == logger) {
        g_logger = NULL;
    }
}

// Set global logger
int logger_set_global(logger_t* logger) {
    g_logger = logger;
    return 0;
}

// Get global logger
logger_t* logger_get_global(void) {
    return g_logger;
}

// Log level to string conversion
const char* log_level_to_string(log_level_t level) {
    if (level >= 0 && level < 4) {
        return LOG_LEVEL_STRINGS[level];
    }
    return "UNKNOWN";
}

// String to log level conversion
log_level_t log_level_from_string(const char* level_str) {
    if (!level_str) return LOG_LEVEL_INFO;
    
    if (strcasecmp(level_str, "DEBUG") == 0) return LOG_LEVEL_DEBUG;
    if (strcasecmp(level_str, "INFO") == 0) return LOG_LEVEL_INFO;
    if (strcasecmp(level_str, "WARN") == 0) return LOG_LEVEL_WARN;
    if (strcasecmp(level_str, "ERROR") == 0) return LOG_LEVEL_ERROR;
    
    return LOG_LEVEL_INFO;
}

// Check if log rotation is needed
static int should_rotate_log(logger_t* logger) {
    return logger->current_file_size >= logger->config.max_file_size;
}

// Perform log rotation
int logger_rotate_if_needed(logger_t* logger) {
    if (!logger || !logger->initialized) {
        return -1;
    }
    
    if (!should_rotate_log(logger)) {
        return 0;  // No rotation needed
    }
    
    return logger_force_rotate(logger);
}

// Force log rotation
int logger_force_rotate(logger_t* logger) {
    if (!logger || !logger->initialized) {
        return -1;
    }
    
    pthread_mutex_lock(&logger->mutex);
    
    // Close current log file
    if (logger->log_file) {
        fclose(logger->log_file);
        logger->log_file = NULL;
    }
    
    // Rotate existing log files
    char old_path[512];
    char new_path[512];
    
    // Move existing rotated logs
    for (int i = logger->config.max_rotated_files - 1; i > 0; i--) {
        snprintf(old_path, sizeof(old_path), "%s.%d", logger->config.log_file_path, i - 1);
        snprintf(new_path, sizeof(new_path), "%s.%d", logger->config.log_file_path, i);
        
        if (access(old_path, F_OK) == 0) {
            rename(old_path, new_path);
        }
    }
    
    // Move current log to .0
    snprintf(new_path, sizeof(new_path), "%s.0", logger->config.log_file_path);
    rename(logger->config.log_file_path, new_path);
    
    // Open new log file
    logger->log_file = fopen(logger->config.log_file_path, "w");
    if (!logger->log_file) {
        pthread_mutex_unlock(&logger->mutex);
        return -1;
    }
    
    logger->current_file_size = 0;
    
    pthread_mutex_unlock(&logger->mutex);
    
    // Log rotation event
    logger_log(logger, LOG_LEVEL_INFO, "LOGGER", "Log file rotated");
    
    return 0;
}

// Format and write log entry
static void write_log_entry(logger_t* logger, log_level_t level, 
                           const char* component, const char* message) {
    if (!logger || !logger->log_file || level < logger->config.min_level) {
        return;
    }
    
    char timestamp[64] = "";
    char thread_info[32] = "";
    
    // Generate timestamp
    if (logger->config.enable_timestamps) {
        time_t now = time(NULL);
        struct tm* tm_info = localtime(&now);
        strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", tm_info);
    }
    
    // Generate thread info
    if (logger->config.enable_thread_id) {
        snprintf(thread_info, sizeof(thread_info), "[%p]", (void*)pthread_self());
    }
    
    // Format log entry
    char log_entry[2048];
    int written = snprintf(log_entry, sizeof(log_entry), 
                          "%s %s [%s] %s: %s\n",
                          timestamp,
                          thread_info,
                          log_level_to_string(level),
                          component ? component : "UNKNOWN",
                          message);
    
    if (written > 0) {
        // Write to file
        fwrite(log_entry, 1, written, logger->log_file);
        fflush(logger->log_file);
        
        // Update file size
        logger->current_file_size += written;
        logger->total_entries++;
        
        // Console output if enabled
        if (logger->config.enable_console_output) {
            fprintf(stderr, "%s", log_entry);
        }
    }
}

// Main logging function
void logger_log(logger_t* logger, log_level_t level, const char* component, 
                const char* format, ...) {
    if (!logger || !logger->initialized) {
        return;
    }
    
    // Format message
    char message[1024];
    va_list args;
    va_start(args, format);
    vsnprintf(message, sizeof(message), format, args);
    va_end(args);
    
    pthread_mutex_lock(&logger->mutex);
    
    // Write log entry
    write_log_entry(logger, level, component, message);
    
    // Check for rotation
    if (should_rotate_log(logger)) {
        pthread_mutex_unlock(&logger->mutex);
        logger_force_rotate(logger);
    } else {
        pthread_mutex_unlock(&logger->mutex);
    }
}

// Log query execution
void logger_log_query(logger_t* logger, const char* query, uint64_t execution_time_ms,
                      uint64_t rows_returned, const char* error_msg) {
    if (!logger || !query) {
        return;
    }
    
    // Truncate query for logging (first 200 chars)
    char query_excerpt[256];
    strncpy(query_excerpt, query, sizeof(query_excerpt) - 1);
    query_excerpt[sizeof(query_excerpt) - 1] = '\0';
    
    // Replace newlines with spaces
    for (char* p = query_excerpt; *p; p++) {
        if (*p == '\n' || *p == '\r' || *p == '\t') {
            *p = ' ';
        }
    }
    
    if (error_msg) {
        logger_log(logger, LOG_LEVEL_ERROR, "QUERY", 
                   "FAILED [%llu ms] [%llu rows] [%s] Query: %s",
                   execution_time_ms, rows_returned, error_msg, query_excerpt);
    } else {
        log_level_t level = (execution_time_ms > 1000) ? LOG_LEVEL_WARN : LOG_LEVEL_INFO;
        logger_log(logger, level, "QUERY", 
                   "SUCCESS [%llu ms] [%llu rows] Query: %s",
                   execution_time_ms, rows_returned, query_excerpt);
    }
}

// Log slow queries
void logger_log_slow_query(logger_t* logger, const char* query, 
                          uint64_t execution_time_ms, uint64_t rows_returned) {
    if (!logger || !query) {
        return;
    }
    
    // Truncate query for logging
    char query_excerpt[512];
    strncpy(query_excerpt, query, sizeof(query_excerpt) - 1);
    query_excerpt[sizeof(query_excerpt) - 1] = '\0';
    
    // Replace newlines with spaces
    for (char* p = query_excerpt; *p; p++) {
        if (*p == '\n' || *p == '\r' || *p == '\t') {
            *p = ' ';
        }
    }
    
    logger_log(logger, LOG_LEVEL_WARN, "SLOW_QUERY", 
               "DETECTED [%llu ms] [%llu rows] Query: %s",
               execution_time_ms, rows_returned, query_excerpt);
}

// Log authentication events
void logger_log_auth_event(logger_t* logger, const char* username, 
                          const char* event, bool success) {
    if (!logger || !username || !event) {
        return;
    }
    
    log_level_t level = success ? LOG_LEVEL_INFO : LOG_LEVEL_WARN;
    logger_log(logger, level, "AUTH", 
               "%s: user=%s, result=%s", 
               event, username, success ? "SUCCESS" : "FAILED");
}

// Log performance metrics
void logger_log_performance(logger_t* logger, const char* metric_name, 
                           double value, const char* unit) {
    if (!logger || !metric_name) {
        return;
    }
    
    logger_log(logger, LOG_LEVEL_INFO, "PERFORMANCE", 
               "%s: %.2f %s", 
               metric_name, value, unit ? unit : "");
}

// Get logger statistics
int logger_get_stats(logger_t* logger, logger_stats_t* stats) {
    if (!logger || !stats) {
        return -1;
    }
    
    pthread_mutex_lock(&logger->mutex);
    
    memset(stats, 0, sizeof(logger_stats_t));
    stats->total_entries = logger->total_entries;
    stats->current_file_size = logger->current_file_size;
    
    pthread_mutex_unlock(&logger->mutex);
    
    return 0;
}